/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.to.aboomy.bannersample.indicator.magicindicator;

import com.to.aboomy.bannersample.ResourceTable;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.ScrollView;
import ohos.agp.components.StackLayout;
import ohos.agp.database.DataSetSubscriber;
import ohos.agp.render.Canvas;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.List;

/**
 * 通用的ViewPager指示器
 *
 * @since 2020-09-29
 */
public class CommonNavigator extends StackLayout implements IpagerNavigator,
        NavigatorHelper.OnNavigatorScrollListener, Component.DrawTask {
    private static final float NUMO5 = 0.5f;
    private static final int NUM2 = 2;
    private ScrollView mScrollView;
    private DirectionalLayout mTitleContainer;
    private DirectionalLayout mIndicatorContainer;
    private IpagerIndicator mIndicator;

    private CommonNavigatorAdapter mAdapter;
    private NavigatorHelper mNavigatorHelper;

    /**
     * 提供给外部的参数配置
     */
    private boolean mIsAdjustMode; // 自适应模式，适用于数目固定的、少量的title
    private boolean mIsEnablePivotScroll; // 启动中心点滚动
    private float mScrollPivotX = NUMO5; // 滚动中心点 0.0f - 1.0f
    private boolean mIsSmoothScroll = true; // 是否平滑滚动，适用于 !mAdjustMode && !mFollowTouch
    private boolean mIsFollowTouch = true; // 是否手指跟随滚动
    private int mRightPadding;
    private int mLeftPadding;
    private boolean mIsIndicatorOnTop; // 指示器是否在title上层，默认为下层
    private boolean mIsSkimOver; // 跨多页切换时，中间页是否显示 "掠过" 效果
    private boolean mIsReselectWhenLayout = true; // PositionData准备好时，是否重新选中当前页，为true可保证在极端情况下指示器状态正确

    // 保存每个title的位置信息，为扩展indicator提供保障
    private List<PositionData> mPositionDataList = new ArrayList<PositionData>();

    private DataSetSubscriber mObserver = new DataSetSubscriber() {
        @Override
        public void onChanged() {
            mNavigatorHelper.setTotalCount(mAdapter.getCount()); // 如果使用helper，应始终保证helper中的totalCount为最新
            init();
        }

        @Override
        public void onInvalidated() {
        }
    };

    /**
     * 构造
     *
     * @param context context
     */
    public CommonNavigator(Context context) {
        super(context);
        mNavigatorHelper = new NavigatorHelper();
        mNavigatorHelper.setNavigatorScrollListener(this);
    }

    @Override
    public void notifyDataSetChanged() {
        if (mAdapter != null) {
            mAdapter.notifyDataSetChanged();
        }
    }

    /**
     * isAdjustMode
     *
     * @return boolean
     */
    public boolean isAdjustMode() {
        return mIsAdjustMode;
    }

    /**
     * setAdjustMode
     *
     * @param isAdjustMode isAdjustMode
     */
    public void setAdjustMode(boolean isAdjustMode) {
        mIsAdjustMode = isAdjustMode;
    }

    /**
     * 获取适配器
     *
     * @return CommonNavigatorAdapter
     */
    public CommonNavigatorAdapter getAdapter() {
        return mAdapter;
    }

    /**
     * 设置适配器
     *
     * @param adapter CommonNavigatorAdapter
     */
    public void setAdapter(CommonNavigatorAdapter adapter) {
        if (mAdapter == adapter) {
            return;
        }
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mObserver);
            mAdapter.unregisterDataSetObserver(mObserver);
        }
        mAdapter = adapter;
        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(mObserver);
            mNavigatorHelper.setTotalCount(mAdapter.getCount());
            if (mTitleContainer != null) { // adapter改变时，应该重新init，但是第一次设置adapter不用，onAttachToMagicIndicator中有init
                mAdapter.notifyDataSetChanged();
            }
        } else {
            mNavigatorHelper.setTotalCount(0);
            init();
        }
    }

    private void init() {
        removeAllComponents();
        Component root;
        if (mIsAdjustMode) {
            root = LayoutScatter.getInstance(getContext())
                    .parse(ResourceTable.Layout_pager_navigator_layout_no_scroll, this, false);
        } else {
            root = LayoutScatter.getInstance(getContext())
                    .parse(ResourceTable.Layout_pager_navigator_layout, this, false);
        }

        mScrollView = (ScrollView) root.findComponentById(ResourceTable.Id_scroll_view);

        mTitleContainer = (DirectionalLayout) root.findComponentById(ResourceTable.Id_title_container);
        mTitleContainer.setPadding(mLeftPadding, 0, mRightPadding, 0);

        mIndicatorContainer = (DirectionalLayout) root.findComponentById(ResourceTable.Id_indicator_container);
        if (mIsIndicatorOnTop) {
            mIndicatorContainer.getComponentParent().moveChildToFront(mIndicatorContainer);
        }
        initTitlesAndIndicator();
        addComponent(root);
        addDrawTask(this);
    }

    /**
     * 初始化title和indicator
     */
    private void initTitlesAndIndicator() {
        for (int ii = 0, jj = mNavigatorHelper.getTotalCount(); ii < jj; ii++) {
            IpagerTitleView view = mAdapter.getTitleView(getContext(), ii);
            if (view instanceof Component) {
                Component component = (Component) view;
                DirectionalLayout.LayoutConfig lp;
                if (mIsAdjustMode) {
                    lp = new DirectionalLayout.LayoutConfig(0, LayoutConfig.MATCH_PARENT);
                    lp.weight = mAdapter.getTitleWeight(getContext(), ii);
                } else {
                    lp = new DirectionalLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_PARENT);
                }
                mTitleContainer.addComponent(component, lp);
            }
        }
        if (mAdapter != null) {
            mIndicator = mAdapter.getIndicator(getContext());
            if (mIndicator instanceof Component) {
                ComponentContainer.LayoutConfig lp = new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                        ComponentContainer.LayoutConfig.MATCH_PARENT);
                mIndicatorContainer.addComponent((Component) mIndicator, lp);
            }
        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mAdapter != null) {
            if (mIndicator != null) {
                mIndicator.onPositionDataProvide(mPositionDataList);
            }
            if (mIsReselectWhenLayout && mNavigatorHelper.getScrollState() == ScrollState.SCROLL_STATE_IDLE) {
                onPageSelected(mNavigatorHelper.getCurrentIndex());
                onPageScrolled(mNavigatorHelper.getCurrentIndex(), 0.0f, 0);
            }
        }
    }

    /**
     * 获取title的位置信息，为打造不同的指示器、各种效果提供可能
     */
    private void preparePositionData() {
        mPositionDataList.clear();
        for (int ii = 0, jj = mNavigatorHelper.getTotalCount(); ii < jj; ii++) {
            PositionData data = new PositionData();
            Component component = mTitleContainer.getComponentAt(ii);
            if (component != null) {
                component.getWidth();
                data.setmLeft(component.getLeft());
                data.setmTop(component.getTop());
                data.setmRight(component.getRight());
                data.setmBottom(component.getBottom());
                if (component instanceof ImeasurablePagerTitleView) {
                    ImeasurablePagerTitleView view = (ImeasurablePagerTitleView) component;
                    data.setmContentLeft(view.getContentLeft());
                    data.setmContentTop(view.getContentTop());
                    data.setmContentRight(view.getContentRight());
                    data.setmContentBottom(view.getContentBottom());
                } else {
                    data.setmContentLeft(data.getmLeft());
                    data.setmContentTop(data.getmTop());
                    data.setmContentRight(data.getmRight());
                    data.setmContentBottom(data.getmBottom());
                }
            }
            mPositionDataList.add(data);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (mAdapter != null) {
            mNavigatorHelper.onPageScrolled(position, positionOffset, positionOffsetPixels);
            if (mIndicator != null) {
                mIndicator.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }
            if (mScrollView != null && mPositionDataList.size() > 0
                    && position >= 0 && position < mPositionDataList.size()) {
                if (mIsFollowTouch) {
                    int minNextPosition = position + 1;
                    if (positionOffsetPixels < 0) {
                        if (position == 0) {
                            minNextPosition = 0;
                        } else {
                            minNextPosition = position - 1;
                        }
                    }
                    int currentPosition = Math.min(mPositionDataList.size() - 1, position);
                    int nextPosition = Math.min(mPositionDataList.size() - 1, minNextPosition);
                    PositionData current = mPositionDataList.get(currentPosition);
                    PositionData next = mPositionDataList.get(nextPosition);
                    float scrollTo = current.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX;
                    float nextScrollTo = next.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX;
                    mScrollView.scrollTo((int) (scrollTo + (nextScrollTo - scrollTo) * positionOffset), 0);
                }
            }
        }
    }

    /**
     * getScrollPivotX
     *
     * @return mScrollPivotX
     */
    public float getScrollPivotX() {
        return mScrollPivotX;
    }

    /**
     * setScrollPivotX
     *
     * @param scrollPivotX scrollPivotX
     */
    public void setScrollPivotX(float scrollPivotX) {
        mScrollPivotX = scrollPivotX;
    }

    @Override
    public void onPageSelected(int position) {
        if (mAdapter != null) {
            preparePositionData();
            mNavigatorHelper.onPageSelected(position);
            if (mIndicator != null) {
                mIndicator.onPageSelected(position);
            }
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (mAdapter != null) {
            mNavigatorHelper.onPageScrollStateChanged(state);
            if (mIndicator != null) {
                mIndicator.onPageScrollStateChanged(state);
            }
        }
    }

    @Override
    public void onAttachToMagicIndicator() {
        init(); // 将初始化延迟到这里
    }

    @Override
    public void onDetachFromMagicIndicator() {
    }

    /**
     * getPagerIndicator
     *
     * @return mIndicator
     */
    public IpagerIndicator getPagerIndicator() {
        return mIndicator;
    }

    /**
     * isEnablePivotScroll
     *
     * @return mIsEnablePivotScroll
     */
    public boolean isEnablePivotScroll() {
        return mIsEnablePivotScroll;
    }

    /**
     * setEnablePivotScroll
     *
     * @param isEnable isEnable
     */
    public void setEnablePivotScroll(boolean isEnable) {
        mIsEnablePivotScroll = isEnable;
    }

    @Override
    public void onEnter(int index, int totalCount, float enterPercent, boolean isLeftToRight) {
        if (mTitleContainer == null) {
            return;
        }
        Component component = mTitleContainer.getComponentAt(index);
        if (component instanceof IpagerTitleView) {
            ((IpagerTitleView) component).onEnter(index, totalCount, enterPercent, isLeftToRight);
        }
    }

    @Override
    public void onLeave(int index, int totalCount, float leavePercent, boolean isLeftToRight) {
        if (mTitleContainer == null) {
            return;
        }
        Component component = mTitleContainer.getComponentAt(index);
        if (component instanceof IpagerTitleView) {
            ((IpagerTitleView) component).onLeave(index, totalCount, leavePercent, isLeftToRight);
        }
    }

    /**
     * isSmoothScroll
     *
     * @return boolean
     */
    public boolean isSmoothScroll() {
        return mIsSmoothScroll;
    }

    /**
     * setSmoothScroll
     *
     * @param isSmoothScroll isSmoothScroll
     */
    public void setSmoothScroll(boolean isSmoothScroll) {
        mIsSmoothScroll = isSmoothScroll;
    }

    /**
     * isFollowTouch
     *
     * @return boolean
     */
    public boolean isFollowTouch() {
        return mIsFollowTouch;
    }

    /**
     * setFollowTouch
     *
     * @param isFollowTouch boolean
     */
    public void setFollowTouch(boolean isFollowTouch) {
        mIsFollowTouch = isFollowTouch;
    }

    /**
     * isSkimOver
     *
     * @return boolean
     */
    public boolean isSkimOver() {
        return mIsSkimOver;
    }

    /**
     * setSkimOver
     *
     * @param isSkimOver boolean
     */
    public void setSkimOver(boolean isSkimOver) {
        mIsSkimOver = isSkimOver;
        mNavigatorHelper.setSkimOver(isSkimOver);
    }

    @Override
    public void onSelected(int index, int totalCount) {
        if (mTitleContainer == null) {
            return;
        }
        Component vv = mTitleContainer.getComponentAt(index);
        if (vv instanceof IpagerTitleView) {
            ((IpagerTitleView) vv).onSelected(index, totalCount);
        }
        if (!mIsAdjustMode && !mIsFollowTouch && mScrollView != null && mPositionDataList.size() > 0) {
            int currentIndex = Math.min(mPositionDataList.size() - 1, index);
            PositionData current = mPositionDataList.get(currentIndex);
            if (mIsEnablePivotScroll) {
                float scrollTo = current.horizontalCenter() - mScrollView.getWidth() * mScrollPivotX;
                if (mIsSmoothScroll) {
                    mScrollView.fluentScrollTo((int) scrollTo, 0);
                } else {
                    mScrollView.scrollTo((int) scrollTo, 0);
                }
            } else {
                // 如果当前项被部分遮挡，则滚动显示完全
                if (mScrollView.getPivotX() + getWidth() > current.getmLeft() + mLeftPadding) {
                    if (mIsSmoothScroll) {
                        mScrollView.fluentScrollTo(current.getmLeft(), 0);
                    } else {
                        mScrollView.scrollTo(current.getmLeft(), 0);
                    }
                } else if (mScrollView.getPivotX() + getWidth() < current.getmRight() - mRightPadding) {
                    if (mIsSmoothScroll) {
                        mScrollView.fluentScrollTo(current.getmRight() - getWidth() / NUM2, 0);
                    } else {
                        mScrollView.scrollTo(current.getmRight() - getWidth(), 0);
                    }
                }
            }
        }
    }

    @Override
    public void onDeselected(int index, int totalCount) {
        if (mTitleContainer == null) {
            return;
        }
        Component component = mTitleContainer.getComponentAt(index);
        if (component instanceof IpagerTitleView) {
            ((IpagerTitleView) component).onDeselected(index, totalCount);
        }
    }

    /**
     * 得到titlsView
     *
     * @param index 索引
     * @return IpagerTitleView
     */
    public IpagerTitleView getPagerTitleView(int index) {
        IpagerTitleView ipagerTitleView = null;
        ipagerTitleView = (IpagerTitleView) mTitleContainer.getComponentAt(index);
        return ipagerTitleView;
    }

    /**
     * getTitleContainer
     *
     * @return mTitleContainer
     */
    public DirectionalLayout getTitleContainer() {
        return mTitleContainer;
    }

    /**
     * getRightPadding
     *
     * @return mRightPadding
     */
    public int getRightPadding() {
        return mRightPadding;
    }

    /**
     * setRightPadding
     *
     * @param rightPadding rightPadding
     */
    public void setRightPadding(int rightPadding) {
        mRightPadding = rightPadding;
    }

    /**
     * getLeftPadding
     *
     * @return mLeftPadding
     */
    public int getLeftPadding() {
        return mLeftPadding;
    }

    /**
     * setLeftPadding
     *
     * @param leftPadding leftPadding
     */
    public void setLeftPadding(int leftPadding) {
        mLeftPadding = leftPadding;
    }

    /**
     * isIndicatorOnTop
     *
     * @return boolean
     */
    public boolean isIndicatorOnTop() {
        return mIsIndicatorOnTop;
    }

    /**
     * setIndicatorOnTop
     *
     * @param isIndicatorOnTop isIndicatorOnTop
     */
    public void setIndicatorOnTop(boolean isIndicatorOnTop) {
        mIsIndicatorOnTop = isIndicatorOnTop;
    }

    /**
     * isReselectWhenLayout
     *
     * @return mIsReselectWhenLayout
     */
    public boolean isReselectWhenLayout() {
        return mIsReselectWhenLayout;
    }

    /**
     * setReselectWhenLayout
     *
     * @param isReselectWhenLayout isReselectWhenLayout
     */
    public void setReselectWhenLayout(boolean isReselectWhenLayout) {
        mIsReselectWhenLayout = isReselectWhenLayout;
    }
}
